﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.IO;
using TCPIPControl.StructHelper;

namespace TcpLabCommon
{
    public delegate void TcpAsynHandler1(NetPacket packet);
    public delegate void TcpAsynHandler2(NetPacketHead packetHead);
    public delegate void WorkerThreadExceptionHandlerDelegate(Exception e);
    /// <summary>
    /// 网络封包Tcp异步服务类
    /// </summary>
    public class Cls_NetPacketTcpAsynService : Cls_NetPacketService
    {

   public      bool ClientNormalExitFlag = true;

        /// <summary>
        /// 发包前事件
        /// </summary>
        public event TcpAsynHandler1 OnBeforeSendPacket;

        /// <summary>
        /// 发包后事件
        /// </summary>
        public event TcpAsynHandler2 OnAfterSendPacket;

        /// <summary>
        /// 收到网络封包后的事件
        /// </summary>
        public event TcpAsynHandler1 OnReceivedPacket;


        public WorkerThreadExceptionHandlerDelegate workerThreadExceptionHandlerDelegate = null;


        public Cls_NetPacketTcpAsynService(NetworkStream netStream) : base(netStream) { ClientNormalExitFlag = true; }

        int num = 0;

        /// <summary>
        /// 该方法的返回值总是为空.提取一个完整网络包然后返回,返回的封包需要通过注册OnReceivedPacket事件的函数获取
        ///     如果收到的包是STRING类型,NetPacket.Data是发送的字符串
        ///     如果收到的包是BINARY类型,NetPacket.Data是发送的文件名长度+文件名+文件内容,请调用ParseFile函数进行解析
        ///     如果收到的包是COMPLEX类型,NetPacket.Data是发序列化的对象，可以直接转型使用
        /// </summary>
        /// <returns></returns>
        public override NetPacket ReceivePacket()
        {
            try
            {
                if (!ClientNormalExitFlag)
                {
                    throw new Exception("下位机正常关闭连接");
                }
                //判断是否满足一个完整封包大小
                if (IsFullNetPacket())//如果有完整封包就返回
                {
                    NetPacket packet = PickNetPacket();
                    if (OnReceivedPacket != null) //判断事件是否注册,如果注册调用回调函数传递收到的封包
                    {
                        OnReceivedPacket(packet);
                    }
                    //return null;//提取到一个封包后应该及时返回
                }
                //【缓冲区不满足一个完整封包大小则继续从网络流读取数据,异步读取】
                _netStream.BeginRead(_tempBuffer, 0, BUFFER_SIZE, new AsyncCallback(AsyncCallbackReadFromNetStream), _netStream);
            }
            catch (Exception ex)
            {
                if (ex.Message != "正常关闭连接")
                {

                }
                else
                {
                    throw new Exception("非正常连接关闭");
                }
                workerThreadExceptionHandlerDelegate.BeginInvoke(ex, null, null);
            }
            return null;
        }

        /// <summary>
        /// 从网络流异步读取数据回调函数
        /// </summary>
        /// <param name="result"></param>
        private void AsyncCallbackReadFromNetStream(IAsyncResult result)
        {
            int readLen = 0;
            try
            {
                NetworkStream netStream = (NetworkStream)result.AsyncState;
                readLen = netStream.EndRead(result);

                //判断读取的字节数+缓冲区已有字节数是否超过缓冲区总大小
                if (readLen + _netDataOffset > _netDataBuffer.Length)
                {
                    if (IsFullNetPacketHead())//如果缓冲区数据满足一个包头数据大小,则可以计算出本次接收的包需要的缓冲区大小,从而实现一次调整大小
                    {
                        Array.Resize<Byte>(ref _netDataBuffer, FullNetPacketSize);
                    }
                    else //不满足一个完整的网络封包的大小
                    {
                        Array.Resize<Byte>(ref _netDataBuffer, _netDataBuffer.Length + BUFFER_SIZE *2);
                    }
                }
                //将新读取的数据拷贝到缓冲区
                Array.Copy(_tempBuffer, 0, _netDataBuffer, _netDataOffset, readLen);
                //Console.WriteLine("_tempBuffer"+ _tempBuffer.Length+"_netDataBuffer:" + _netDataBuffer.Length+ " _netDataOffset:"+ _netDataOffset+ " readLen:"+ readLen);
                //修改"网络数据实际长度"
                _netDataOffset += readLen;
               // Console.WriteLine("缓冲区长度：" + _tempBuffer.Length + " ,网络缓冲区长度：" + _netDataBuffer.Length);
                ReceivePacket();
        

            }
            catch (Exception ex)
            {
                workerThreadExceptionHandlerDelegate.BeginInvoke(ex, null, null);
            }
        }

        /// <summary>
        /// 发包[发送Tcp包/发送Udp数据报]
        /// </summary>
        /// <param name="packet"></param>
        protected override void SendPacket(NetPacket packet)
        {
            if (packet == null || packet.Data == null || packet.PacketHead == null)
                return;

            if (OnBeforeSendPacket != null)
                OnBeforeSendPacket(packet);

            MemoryStream mStream = new MemoryStream();

            byte[] buffer = null;
            #region【计算包体长度】
            if (packet.PacketHead.Len == 0)
            {
                switch (packet.PacketHead.PType)
                {
                    case PacketType.STRING:
                        string a = packet.Data as string;
                        buffer = Serializehelper<StringSerializeHelper>().Serialize(a);
                        packet.PacketHead.Len = buffer.Length;
                        break;
                    case PacketType.ACQParametersConfig:
                        ACQParametersConfig acq = (ACQParametersConfig)packet.Data;
                        buffer = Serializehelper<StructHelper>().Serialize(acq);
                        packet.PacketHead.Len = buffer.Length;
                        break;
                    case PacketType.AccelerationPacket:
                        AccData acc = (AccData)packet.Data;
                        buffer = Serializehelper<StructHelper>().Serialize(acc);
                        packet.PacketHead.Len = buffer.Length;
                        break;
                    case PacketType.DisplacementPacket:
                        DisplacementData dataPacket = (DisplacementData)packet.Data;
                        buffer = Serializehelper<StructHelper>().Serialize(dataPacket);
                        packet.PacketHead.Len = buffer.Length;
                        break;
                    case PacketType.SlowSignalPacket:
                        SlowSignal slow = (SlowSignal)packet.Data;
                        buffer = Serializehelper<StructHelper>().Serialize(slow);
                        packet.PacketHead.Len = buffer.Length;
                        break;
                    case PacketType.SpeedPacket:
                        SpeedData speed = (SpeedData)packet.Data;
                        buffer = Serializehelper<StructHelper>().Serialize(speed);
                        packet.PacketHead.Len = buffer.Length;
                        break;
                    case PacketType.SamplingPacket:
                        SamplingConfig speedreal = (SamplingConfig)packet.Data;
                        buffer = Serializehelper<StructHelper>().Serialize(speedreal);
                        packet.PacketHead.Len = buffer.Length;
                        break;
                    case PacketType.TimeSetting:
                        MachineTime machinetime = (MachineTime)packet.Data;
                        buffer = Serializehelper<StructHelper>().Serialize(machinetime);
                        packet.PacketHead.Len = buffer.Length;
                        break;
                    case PacketType.TimeUpload:
                        MachineTime machinetime1 = (MachineTime)packet.Data;
                        buffer = Serializehelper<StructHelper>().Serialize(machinetime1);
                        packet.PacketHead.Len = buffer.Length;
                        break;
                    default:
                        break;
                }
            }

            #endregion

            #region【写入包头】
            mStream.Write(BitConverter.GetBytes(packet.PacketHead.Version), 0, Marshal.SizeOf(packet.PacketHead.Version));
            mStream.Write(BitConverter.GetBytes((Int32)packet.PacketHead.PType), 0, sizeof(Int32));
            mStream.Write(BitConverter.GetBytes(packet.PacketHead.Len), 0, Marshal.SizeOf(packet.PacketHead.Len));
            #endregion

            #region【写入包体】
            //  byte[] buffer = null;
            //switch (packet.PacketHead.PType)
            //{
            //    case PacketType.STRING:
            //        buffer = Encoding.Default.GetBytes(Convert.ToString(packet.Data));
            //        break;
            //    case PacketType.BINARY:
            //        buffer = (byte[])packet.Data;
            //        break;
            //    case PacketType.COMPLEX:
            //        MemoryStream m = new MemoryStream();
            //        SerializeHelper<StructHelper>()(m, packet.Data);
            //        m.Position = 0;
            //        buffer = new byte[m.Length];
            //        m.Read(buffer, 0, (Int32)m.Length);
            //        break;
            //}
            if (buffer != null)
                mStream.Write(buffer, 0, buffer.Length);

            #endregion

            #region【将内存流一次写入网络流,异步写入】
            mStream.Seek(0, SeekOrigin.Begin);
            _netStream.BeginWrite(mStream.GetBuffer(), 0, (Int32)mStream.Length, new AsyncCallback(AsyncCallbackWriteToNetStream), new WriteNetStreamASyncCallbackParam { netStream = _netStream, packetHead = packet.PacketHead });
            #endregion
        }

        /// <summary>
        /// 写入网络流异步回调函数
        /// </summary>
        /// <param name="result"></param>
        private void AsyncCallbackWriteToNetStream(IAsyncResult result)
        {
            try
            {
                WriteNetStreamASyncCallbackParam p = (WriteNetStreamASyncCallbackParam)result.AsyncState;
                p.netStream.EndWrite(result);
                if (OnAfterSendPacket != null)//事件处理,如果注册了该事件,则执行回调函数
                    OnAfterSendPacket(p.packetHead);
            }
            catch (Exception ex)
            {
                Console.WriteLine("写入网络流异步回调函数发生错误："+ ex.Message);
                //string a = e.Message.ToString();
            }
        }
    }

    /// <summary>
    /// 写入网络流异步回调参数
    /// </summary>
    sealed class WriteNetStreamASyncCallbackParam
    {
        /// <summary>
        /// 网络流
        /// </summary>
        internal NetworkStream netStream;
        /// <summary>
        /// 包头
        /// </summary>
        internal NetPacketHead packetHead;
    }
}
